using System;
using System.Collections.Generic;
using System.Linq;

namespace NumSkull
{
    public class HistogramBin
    {
        private readonly List<HistogramBinValue> _values = new List<HistogramBinValue>();
        private readonly bool _dynamic;
        private readonly Bounds _initialBounds;

        public HistogramBin(double value, bool dynamic = false)
            : this(value, new Bounds(value, value))
        {
            _dynamic = dynamic;
        }

        public HistogramBin(double value, Bounds bounds)
            : this(bounds)
        {
            if (!bounds.IsInRange(value)) throw new ArgumentOutOfRangeException("value");
            _values.Add(new HistogramBinValue(value));
        }

        public HistogramBin(Bounds bounds)
        {
            if (bounds == null) throw new ArgumentNullException("bounds");
            _initialBounds = bounds;
        }

        public IList<HistogramBinValue> Values { get { return _values; } }
        
        public int Count
        {
            get
            {
                if (_values.Count == 0) return 0;

                var count = _values.Select(s => s.Count).Aggregate((total, next) => total + next);
                return count;
            }
        }
        
        public Bounds Bounds
        {
            get
            {
                if (_dynamic)
                {
                    var bounds = new Bounds(_values.Select(d => d.Value).Min(), _values.Select(d => d.Value).Max());
                    return bounds;
                }

                return _initialBounds;
            }
        }

        public double Range
        {
            get
            {
                var range = _values.Select(d => d.Value).Max() - Values.Select(d => d.Value).Min();
                return range;
            }
        }

        public void Add(IEnumerable<double> values)
        {
            if (_dynamic)
            {
                values.ForEach(Add);
                return;
            }

            var filtered = values.Where(Bounds.IsInRange);
            filtered.ForEach(Add);
        }

        public void Add(double value)
        {
            if (_dynamic || Bounds.IsInRange(value))
            {
                var found = _values.Find(v => Equals(v.Value, value));
                if (found != null)
                {
                    found.Count++;
                    return;
                }

                _values.Add(new HistogramBinValue(value));
            }
        }
    }
}